package dm.ast;

import test.envset.GlobalState;

import java.util.ArrayList;
import java.util.List;

/**
 * @author weihuixin
 * @date 2024/2/27
 * @Description
 */
public class DmWith extends DmASTNode {

    public final static String CTE_NAME_PRE = " CTE_";

    //存储子查询对象的列表
    public List<DmSelect> CTEs = null;
    //存储子查询名称的列表
    public List<String> ctes_name = null;
    //存储子查询列信息的列表
    public List<List<DmColumn>> ctes_cols = null;
    //cte_size 表示当前 DmWith 对象中 CTEs（子查询对象列表）的数量,即包含了多少个子查询
    public int cte_size = 0;
    //用于表示每个子查询是否被使用过
    public int used_ctes = 0;

    public DmWith() {
        CTEs = new ArrayList<DmSelect>();
        ctes_name = new ArrayList<String>();
        ctes_cols = new ArrayList<List<DmColumn>>();
        cte_size = 0;
        used_ctes = 0;
    }

    //添加一个子查询
    public void addCTE(DmSelect s) {
        CTEs.add(s);
        ctes_name.add(String.format("%s%d", CTE_NAME_PRE, cte_size));
        cte_size++;
        //将子查询的列信息取出,构造临时列名
        buildCTEcolumns(s);
    }

    //标记一个 CTE 未被使用
    public void flagUnusedCTE(int i) {
        if (i < cte_size && cte_size != 0) {
            CTEs.set(i, null);
            ctes_name.set(i, null);
            ctes_cols.set(i, null);
        }
    }

    //构造 CTE 的列信息
    private void buildCTEcolumns(DmSelect s) {
        List<DmColumn> cols = new ArrayList<DmColumn>();
        List<DmExpression> elements = s.getSelectElements().elements;
        for (int i = 0; i<elements.size(); i++) {
            DmExpression ele = elements.get(i);
            String colname = String.format("C%d", i);
            DmColumn col = new DmColumn(ctes_name.get(cte_size - 1), colname, ele.reType);
            cols.add(col);
        }
        ctes_cols.add(cols);
    }

    @Override
    public void setWidt() {
        wid_t = 1;
        for (int i = 0; i < cte_size; i++) {
            CTEs.get(i).setWidt();
            wid_t += CTEs.get(i).wid_t;
        }
    }

    @Override
    public void setDept() {
        int tmpMAXdept = 1;
        for (int i = 0; i < cte_size; i++) {
            CTEs.get(i).setDept();
            if (CTEs.get(i).dep_t > tmpMAXdept) {
                tmpMAXdept = CTEs.get(i).dep_t;
            }
        }
        dep_t = tmpMAXdept;
    }

    @Override
    public DmDataType getExpressionType() {
        return null;
    }

    //转化为可执行sql
    private String cteToSqlLow(int seq) {
        StringBuilder sql = new StringBuilder();
        sql.append(ctes_name.get(seq));
        //增加列信息
        sql.append("(");
        List<DmColumn> cols = ctes_cols.get(seq);
        sql.append(cols.get(0).name);
        for (int i = 1; i < cols.size(); i++) {
            sql.append("," + cols.get(i).name);
        }
        sql.append(") AS (");
        sql.append(CTEs.get(seq).toSql());
        sql.append(")");
        return sql.toString();
    }

    private String ctesToSql() {
        StringBuilder sql = new StringBuilder();
        sql.append(cteToSqlLow(0));
        for (int i = 1; i < cte_size; i++) {
            sql.append("," + cteToSqlLow(i));
        }
        return sql.toString();
    }

    @Override
    public String toSql() {
        if (cte_size == 0) {
            return " " + this.getClass().getSimpleName();
        } else {
            return "WITH /* ctes :" + cte_size + "*/" + ctesToSql();
        }
    }

    @Override
    public void fill(GlobalState globalState) {
        if (!isCompleted) {
            for (int i = 0; i < cte_size; i++) {
                CTEs.get(i).fill(globalState);
            }
            isCompleted = true;
        }
    }

    //处理sq1错误
//-4097|单行子查询返回多行
    public void fixErr4097() {
        for (int i = 0; i < cte_size; i++) {
            CTEs.get(i).fixErr4097();
        }
    }

    //通过去除未使用的 CTE ,构造一个新的 dmwith 结构
    public DmWith buildNewDmWith() {
        if (used_ctes == 0) {//如果没有已使用的 CTE ,那么直接返回 null
            return null;
        }
        if (used_ctes == cte_size) {//如果所有 CTE 均已使用,那么直接返回自身
            return this;
        }
        DmWith nWith = new DmWith();
        for (int i = 0; i < cte_size; i++) {
            if (CTEs.get(i) != null) {
                nWith.CTEs.add(CTEs.get(i));
                nWith.ctes_name.add(ctes_name.get(i));
                nWith.ctes_cols.add(ctes_cols.get(i));
                nWith.cte_size++;
                nWith.used_ctes++;
            }
        }
        return nWith;
    }
}


